import {Database, Statement} from 'better-sqlite3';
import {SqlExecuteResult} from "./types";
import {ConditionBuilder} from "../conditions";
import {DeleteBuilder, InsertBuilder, SelectBuilder, SqlBuildResult, TableBuilder, UpdateBuilder} from "../builders";
import {Transaction} from "./Transaction";

/**
 * SQL执行器
 * 封装对better-sqlite3的调用，提供SQL执行功能
 */
export class SqlExecutor {

  // 内部数据库连接实例
  private _db: Database;

  // 预处理语句缓存
  private statementCache: Map<string, Statement> = new Map();

  // 最大缓存语句数量
  private maxCacheSize: number = 100;

  // 是否开启调试模式
  private debug: boolean = false;

  /**
   * 构造函数
   * @param db better-sqlite3数据库实例
   * @param options 选项配置
   * @param options.debug 是否启用调试模式，启用后会在控制台输出SQL语句和参数
   * @param options.maxCacheSize 最大缓存语句数量，默认100条
   *
   * @example
   * ```typescript
   * import Database from 'better-sqlite3';
   * import { SqlExecutor } from 'quicklider';
   *
   * // 创建数据库连接
   * const db = new Database(':memory:');
   *
   * // 创建SQL执行器
   * const executor = new SqlExecutor(db, { debug: true });
   * ```
   */
  constructor(db: Database, options?: { debug?: boolean; maxCacheSize?: number }) {
    if (!db) {
      throw new Error('Database connection cannot be null');
    }

    this._db = db;
    this.debug = options?.debug || false;

    if (options?.maxCacheSize && options.maxCacheSize > 0) {
      this.maxCacheSize = options.maxCacheSize;
    }
  }

  /**
   * 创建事务管理器实例
   *
   * @returns 事务管理器实例
   *
   * @example
   * ```typescript
   * // 创建事务管理器
   * const tx = executor.transaction();
   *
   * // 使用事务执行操作
   * tx.run(() => {
   *   // 在事务中执行操作，需要使用执行器
   *   executor.executeSql('INSERT INTO users (name) VALUES (@name)', { name: 'Alice' });
   *   const userId = executor.db.lastInsertRowid;
   *
   *   executor.executeSql('INSERT INTO profiles (user_id, bio) VALUES (@id, @bio)',
   *     { id: userId, bio: 'New user profile' }
   *   );
   *
   *   return userId;
   * });
   * ```
   */
  public transaction(): Transaction {
    return new Transaction(this._db, this.debug);
  }

  /**
   * 执行SQL查询语句并返回多行结果
   *
   * @param sql SQL查询语句，支持命名参数（以@开头）或索引参数（?）
   * @param params 参数对象，可以是命名参数对象或数组
   * @returns 查询结果数组
   *
   * @example 使用命名参数
   * ```typescript
   * // 使用命名参数（推荐）
   * const users = executor.query<User>(
   *   'SELECT * FROM users WHERE age > @minAge AND status = @status',
   *   { minAge: 18, status: 'active' }
   * );
   * ```
   *
   * @example 使用索引参数
   * ```typescript
   * // 使用索引参数（?占位符）
   * const users = executor.query<User>(
   *   'SELECT * FROM users WHERE age > ? AND status = ?',
   *   [18, 'active']
   * );
   * ```
   */
  public query<T = any>(sql: string, params?: Record<string, any> | any[]): T[] {
    this.logSql(sql, params);

    try {
      const stmt = this.prepareStatement(sql);
      return stmt.all(params || {}) as T[];
    } catch (err) {
      this.handleError(err, sql, params);
      throw err;
    }
  }

  /**
   * 执行SQL查询语句并返回单行结果
   *
   * @param sql SQL查询语句，支持命名参数（以@开头）或索引参数（?）
   * @param params 参数对象，可以是命名参数对象或数组
   * @returns 单行查询结果或undefined（如果没有匹配记录）
   *
   * @example 使用命名参数
   * ```typescript
   * // 查询单个用户
   * const user = executor.queryOne<User>(
   *   'SELECT * FROM users WHERE id = @userId',
   *   { userId: 1 }
   * );
   *
   * if (user) {
   *   console.log(`Found user: ${user.username}`);
   * } else {
   *   console.log('User not found');
   * }
   * ```
   */
  public queryOne<T = any>(sql: string, params?: Record<string, any> | any[]): T | undefined {
    this.logSql(sql, params);

    try {
      const stmt = this.prepareStatement(sql);
      return stmt.get(params || {}) as T | undefined;
    } catch (err) {
      this.handleError(err, sql, params);
      throw err;
    }
  }

  /**
   * 执行SQL语句（插入、更新、删除等）
   *
   * @param sql SQL语句，支持命名参数（以@开头）或索引参数（?）
   * @param params 参数对象，可以是命名参数对象或数组
   * @returns 执行结果，包含受影响行数和最后插入的ID
   *
   * @example 插入数据
   * ```typescript
   * // 插入数据
   * const result = executor.executeSql(
   *   'INSERT INTO users (username, email, age) VALUES (@username, @email, @age)',
   *   { username: 'john', email: 'john@example.com', age: 25 }
   * );
   *
   * console.log(`Inserted user ID: ${result.lastInsertRowid}`);
   * console.log(`Affected rows: ${result.changes}`);
   * ```
   *
   * @example 更新数据
   * ```typescript
   * // 更新数据
   * const result = executor.executeSql(
   *   'UPDATE users SET status = @status WHERE last_login < @date',
   *   { status: 'inactive', date: '2023-01-01' }
   * );
   *
   * console.log(`Updated ${result.changes} users`);
   * ```
   */
  public executeSql(sql: string, params?: Record<string, any> | any[]): SqlExecuteResult {
    this.logSql(sql, params);

    try {
      const stmt = this.prepareStatement(sql);
      const result = stmt.run(params || {});

      return {
        changes: result.changes,
        lastInsertRowid: result.lastInsertRowid
      };
    } catch (err) {
      this.handleError(err, sql, params);
      throw err;
    }
  }

  /**
   * 通用执行方法，可接收SQL字符串、参数或各种构建器
   * 自动识别参数类型并执行相应操作
   *
   * @param input SQL字符串或构建器实例
   * @param params 当input为SQL字符串时的参数对象
   * @returns 执行结果，类型取决于输入的构建器类型
   *
   * @example SQL字符串和参数
   * ```typescript
   * // 执行SQL字符串
   * const result = executor.execute(
   *   'INSERT INTO users (username) VALUES (@username)',
   *   { username: 'john' }
   * );
   * ```
   *
   * @example 查询构建器
   * ```typescript
   * // 执行查询构建器
   * const users = executor.execute(
   *   SqlBuilder.create('user').select().where('status', '=', 'active')
   * );
   * ```
   *
   * @example 插入构建器
   * ```typescript
   * // 执行插入构建器
   * const result = executor.execute(
   *   SqlBuilder.create('user').insert({ username: 'john', email: 'john@example.com' })
   * );
   * ```
   *
   * @example 更新构建器
   * ```typescript
   * // 执行更新构建器
   * const result = executor.execute(
   *   SqlBuilder.create('user').update({ status: 'inactive' }).where('id', '=', 1)
   * );
   * ```
   *
   * @example 删除构建器
   * ```typescript
   * // 执行删除构建器
   * const result = executor.execute(
   *   SqlBuilder.create('user').delete().where('status', '=', 'deleted')
   * );
   * ```
   *
   * @example 表构建器
   * ```typescript
   * // 执行表构建器
   * const result = executor.execute(
   *   TableBuilder.create('users').ifNotExist().autoId().text('username')
   * );
   * ```
   */
  public execute<T = any>(
    input: string | SelectBuilder<T> | InsertBuilder<T> | UpdateBuilder<T> | DeleteBuilder<T> | TableBuilder<T> | ConditionBuilder<T>,
    params?: Record<string, any> | any[]
  ): any {
    // 如果是字符串，按原来的方式执行SQL
    if (typeof input === 'string') {
      return this.executeSql(input, params);
    }

    // 根据构建器类型执行对应操作
    if (input instanceof SelectBuilder) {
      // 对于查询构建器，检查是否设置了single()
      const result = input.build();
      if (input.isSingleResult()) {
        return this.queryOne<T>(result.sql, result.params);
      } else {
        return this.query<T>(result.sql, result.params);
      }
    } else if (input instanceof InsertBuilder) {
      const result = input.build();
      return this.executeSql(result.sql, result.params);
    } else if (input instanceof UpdateBuilder) {
      const result = input.build();
      return this.executeSql(result.sql, result.params);
    } else if (input instanceof DeleteBuilder) {
      const result = input.build();
      return this.executeSql(result.sql, result.params);
    } else if (input instanceof TableBuilder) {
      const result = input.build() as SqlBuildResult & { indexSqls: string[] };

      // 执行创建表语句
      const tableResult = this.executeSql(result.sql, result.params);

      // 执行创建索引语句
      for (const indexSql of result.indexSqls) {
        this.executeSql(indexSql, {});
      }

      return tableResult;
    } else if (input instanceof ConditionBuilder) {
      // 使用类型断言，因为我们知道实际上ConditionBuilder的实现具有build方法
      const result = (input as any).build();
      return this.executeSql(result.sql, result.params);
    }

    // 如果不是已知类型，则抛出错误
    throw new Error('Unsupported input type for execute method');
  }

  /**
   * 执行批处理操作
   * 一次性执行多个SQL语句，提高性能
   *
   * @param operations 操作数组，每个操作包含SQL和参数
   * @returns 执行结果数组
   *
   * @example
   * ```typescript
   * // 批量执行插入操作
   * const results = executor.batch([
   *   {
   *     sql: 'INSERT INTO users (username) VALUES (@username)',
   *     params: { username: 'user1' }
   *   },
   *   {
   *     sql: 'INSERT INTO users (username) VALUES (@username)',
   *     params: { username: 'user2' }
   *   },
   *   {
   *     sql: 'UPDATE users SET status = @status WHERE id = @id',
   *     params: { status: 'active', id: 1 }
   *   }
   * ]);
   *
   * // 查看每个操作的结果
   * results.forEach((result, index) => {
   *   console.log(`Operation ${index}: ${result.changes} rows affected`);
   * });
   * ```
   */
  public batch(operations: { sql: string, params?: Record<string, any> | any[] }[]): SqlExecuteResult[] {
    if (!operations || operations.length === 0) {
      return [];
    }

    const results: SqlExecuteResult[] = [];
    const tx = this.transaction();

    // 在事务中执行所有操作
    tx.run(() => {
      for (const op of operations) {
        const result = this.executeSql(op.sql, op.params);
        results.push(result);
      }
    });

    return results;
  }

  /**
   * 批量执行构建器
   * 在单个事务中执行多个构建器实例
   *
   * @param builders 构建器数组，可以是任何继承自ConditionBuilder的构建器
   * @returns 执行结果数组
   *
   * @example
   * ```typescript
   * // 批量执行多个构建器
   * const results = executor.batchBuilders([
   *   userSql.insert({ username: 'user1' }),
   *   userSql.insert({ username: 'user2' }),
   *   userSql.update({ status: 'active' }).where('id', '=', 1)
   * ]);
   *
   * // 查看结果
   * results.forEach((result, index) => {
   *   console.log(`Operation ${index}: ${result.changes} rows affected`);
   * });
   * ```
   */
  public batchBuilders(builders: ConditionBuilder<any>[]): SqlExecuteResult[] {
    if (!builders || builders.length === 0) {
      return [];
    }

    const results: SqlExecuteResult[] = [];
    const tx = this.transaction();

    // 在事务中执行所有构建器
    tx.run(() => {
      for (const builder of builders) {
        // 使用类型断言，因为我们知道实际上ConditionBuilder的实现具有build方法
        const result = (builder as any).build();
        const execResult = this.executeSql(result.sql, result.params);
        results.push(execResult);
      }
    });

    return results;
  }

  /**
   * 获取预处理语句
   * @param sql SQL语句
   * @returns 预处理语句对象
   * @internal
   */
  private prepareStatement(sql: string): Statement {
    // 优先从缓存获取
    let stmt = this.statementCache.get(sql);

    // 如果不存在，创建并缓存
    if (!stmt) {
      stmt = this._db.prepare(sql);

      // 如果缓存达到最大值，清除一半缓存
      if (this.statementCache.size >= this.maxCacheSize) {
        const keys = Array.from(this.statementCache.keys());
        const halfSize = Math.floor(this.maxCacheSize / 2);

        for (let i = 0; i < halfSize; i++) {
          this.statementCache.delete(keys[i]);
        }
      }

      this.statementCache.set(sql, stmt);
    }

    return stmt;
  }

  /**
   * 记录SQL日志
   * @param sql SQL语句
   * @param params 参数对象
   * @internal
   */
  private logSql(sql: string, params?: Record<string, any> | any[]): void {
    if (this.debug) {
      console.log(`SQL: ${sql}`);
      if (params) {
        console.log('Params:', JSON.stringify(params, null, 2));
      }
    }
  }

  /**
   * 处理SQL错误
   * @param err 错误对象
   * @param sql SQL语句
   * @param params 参数对象
   * @internal
   */
  private handleError(err: any, sql: string, params?: Record<string, any> | any[]): void {
    const errorMessage = err?.message || String(err) || 'Unknown error';
    console.error('SQL Error:', errorMessage);
    console.error('SQL:', sql);
    if (params) {
      console.error('Params:', JSON.stringify(params, null, 2));
    }

    // 增强错误信息
    if (err) {
      err.message = `SQL execution error: ${errorMessage}\nSQL: ${sql}`;
    }
  }

  /**
   * 清除语句缓存
   * 在修改表结构后可能需要调用此方法清除缓存的预处理语句
   *
   * @example
   * ```typescript
   * // 修改表结构后清除缓存
   * executor.execute('ALTER TABLE users ADD COLUMN address TEXT');
   * executor.clearStatementCache();
   * ```
   */
  public clearStatementCache(): void {
    this.statementCache.clear();
  }

  /**
   * 获取数据库实例
   * 允许访问底层数据库对象
   * 例如使用 db.lastInsertRowid 获取最后插入的行ID
   */
  public get db(): Database {
    return this._db;
  }
}
